﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Net.Mime;

namespace Duellum.PackagerCmd
{
	/*
	public class Pair<T>
	{
		public T One { get; set; }
		public T Two { get; set; }
		
		public Pair(T one, T two)
		{
			One = one;
			Two = two;
		}
	}

	public static class Pair
	{
		static public Pair<T> New<T>(T one, T two)
		{
			return new Pair<T>(one, two);
		}
	}

	public class Tuple<T1, T2>
	{
		public T1 One { get; set; }
		public T2 Two { get; set; }
		
		public Tuple(T1 one, T2 two)
		{
			One = one;
			Two = two;
		}
	}//*/

	public static class Packager
	{
		public struct PartInfo
		{
			public string FullPath { get; private set; }
			public string RelativePath { get; private set; }
			public string MimeType { get; private set; }
			
			public PartInfo(string fullPath, string relativePath) : this()
			{
				FullPath = fullPath;
				RelativePath = relativePath;
				MimeType = ResolveMimeType(Path.GetExtension(fullPath));
			}
			
			static public string ResolveMimeType(string extension)
			{
				switch (extension) {
					case ".txt":	return MediaTypeNames.Text.Plain;
					case ".xml":	return MediaTypeNames.Text.Xml;
					case ".htm":
					case ".html":	return MediaTypeNames.Text.Html;
					case ".gif":	return MediaTypeNames.Image.Gif;
					case ".jpg":
					case ".jpeg":	return MediaTypeNames.Image.Jpeg;
					case ".tiff":	return MediaTypeNames.Image.Tiff;
					case ".pdf":	return MediaTypeNames.Application.Pdf;
					case ".rtf":	return MediaTypeNames.Application.Rtf;
					case ".zip":	return MediaTypeNames.Application.Zip;
					default:		return MediaTypeNames.Application.Octet;
				}
			}

			//public const string Octet = "application/octet-stream";
			//public const string Pdf = "application/pdf";
			//public const string Rtf = "application/rtf";
			//public const string Soap = "application/soap+xml";
			//public const string Zip = "application/zip";

			//public const string Gif = "image/gif";
			//public const string Jpeg = "image/jpeg";
			//public const string Tiff = "image/tiff";

			//public const string Html = "text/html";
			//public const string Plain = "text/plain";
			//public const string RichText = "text/richtext";
			//public const string Xml = "text/xml";			}
		}

		public const string PART_RELATIONSHIP_TYPE = "file";
		
		static public void CreatePackage(string packagePath, string rootFolderPath)
		{
			int rootLen = rootFolderPath.Length;
			
			var infos = GetAllFiles(rootFolderPath).Select(f => new PartInfo(f, f.Substring(rootLen))).ToArray();
			
			CreatePackage(packagePath, infos);
		}

		static public IEnumerable<string> GetAllFiles(string folderPath)
		{
			if (!Directory.Exists(folderPath)) {
				return Enumerable.Empty<string>();
			} else {
				var files = Directory.GetFiles(folderPath);
				var folders = Directory.GetDirectories(folderPath);
				
				return
					files
					.Where(f => (File.GetAttributes(f) & FileAttributes.Hidden) == 0)
					.Union(
						folders
						.Where(f => (File.GetAttributes(f) & FileAttributes.Hidden) == 0)
						.SelectMany(subfolderPath => GetAllFiles(subfolderPath))
					);
			}
		}
		
		private static void CreatePackage(string packagePath, IEnumerable<PartInfo> partInfos)
		{
			//var partUris = partInfos.Select(part => PackUriHelper.CreatePartUri(new Uri(part.RelativePath, UriKind.Relative))).ToArray();
			
			using (Package package = Package.Open(packagePath, FileMode.Create))
			{
				foreach (var partInfo in partInfos) {
					var partUri = PackUriHelper.CreatePartUri(new Uri(partInfo.RelativePath, UriKind.Relative));
					
					var part = package.CreatePart(partUri, partInfo.MimeType, CompressionOption.Maximum);
					
					using (FileStream fileStream = new FileStream(partInfo.FullPath, FileMode.Open, FileAccess.Read))
					{
						CopyStreams(fileStream, part.GetStream());
					}
					
					package.CreateRelationship(part.Uri, TargetMode.Internal, PART_RELATIONSHIP_TYPE);
				}
				/*
				PackagePart packagePartDocument =
					package.CreatePart(partUriDocument,
								   System.Net.Mime.MediaTypeNames.Text.Plain);

				// Copy the data to the Document Part
				using (FileStream fileStream = new FileStream(
					   documentPath, FileMode.Open, FileAccess.Read))
				{
					CopyStream(fileStream, packagePartDocument.GetStream());
				}// end:using(fileStream) - Close and dispose fileStream.

				// Add a Package Relationship to the Document Part
				package.CreateRelationship(packagePartDocument.Uri,
										   TargetMode.Internal,
										   PackageRelationshipType);

				// Add a Resource Part to the Package
				PackagePart packagePartResource =
					package.CreatePart(partUriResource,
								   System.Net.Mime.MediaTypeNames.Image.Gif);

				// Copy the data to the Resource Part
				using (FileStream fileStream = new FileStream(
					   resourcePath, FileMode.Open, FileAccess.Read))
				{
					CopyStream(fileStream, packagePartResource.GetStream());
				}// end:using(fileStream) - Close and dispose fileStream.

				//// Add Relationship from the Document part to the Resource part
				//packagePartDocument.CreateRelationship(
				//                        partUriResource,
				//                        TargetMode.Internal,
				//                        ResourceRelationshipType);
				package.CreateRelationship(packagePartResource.Uri,
										   TargetMode.Internal,
										   PackageRelationshipType);
				//*/
			}
		}
		
		//  --------------------------- CopyStream ---------------------------
		/// <summary>
		///   Copies data from a source stream to a target stream.</summary>
		/// <param name="source">
		///   The source stream to copy from.</param>
		/// <param name="target">
		///   The destination stream to copy to.</param>
		private static void CopyStreams(Stream source, Stream target)
		{
			const int bufSize = 0x1000;
			byte[] buf = new byte[bufSize];
			int bytesRead = 0;
			while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
				target.Write(buf, 0, bytesRead);
		}// end:CopyStream()
	}
}
